/**
 * MCU 适配层 - 通用基础功能
 */

#include <xil_io.h>
#include "mcu.h"

#include "share/log.h"
#define LOGLEVEL LOG_DEBUG
#define LOGTAG   "MCU"
#include "share/log4app.h"

// Xilinx-A9 是小端模式
bool mcu_is_big_endian() { return false; }

size_t mcu_align_bytes() {
	// Xilinx-A9 系列 MCU 是 4 字节对齐
	// 如果地址未对齐，不可访问 16/32 位变量
	return 4;
}

uint64_t mcu_get_beu64(const void *stream) {
	uint8_t *buf = (uint8_t*)stream;
	return ((uint64_t)buf[0] << 56) | ((uint64_t)buf[1] << 48) | ((uint64_t)buf[2] << 40) | ((uint64_t)buf[3] << 32) |
	       ((uint64_t)buf[4] << 24) | ((uint64_t)buf[5] << 16) | ((uint64_t)buf[6] << 8 ) | ((uint64_t)buf[7]);
}

uint32_t mcu_get_beu32(const void *stream) {
	uint8_t *buf = (uint8_t*)stream;
	return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3]);
}

uint16_t mcu_get_beu16(const void *stream) {
	uint8_t *buf = (uint8_t*)stream;
	return (buf[0] << 8) | (buf[1]);
}

uint64_t mcu_get_leu64(const void *stream) {
	uint8_t *buf = (uint8_t*)stream;
	return ((uint64_t)buf[7] << 56) | ((uint64_t)buf[6] << 48) | ((uint64_t)buf[5] << 40) | ((uint64_t)buf[4] << 32) |
	       ((uint64_t)buf[3] << 24) | ((uint64_t)buf[2] << 16) | ((uint64_t)buf[1] << 8 ) | ((uint64_t)buf[0]);
}

uint32_t mcu_get_leu32(const void *stream) {
	uint8_t *buf = (uint8_t*)stream;
	return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]);
}

uint16_t mcu_get_leu16(const void *stream) {
	uint8_t *buf = (uint8_t*)stream;
	return (buf[1] << 8) | (buf[0]);
}

void* mcu_get_pointer(const void *stream) {
	// Xilinx-A9 是小端模式
	switch(mcu_pointer_size()) {
	case 8:  return (void*)(uintptr_t)mcu_get_leu64(stream);
	case 4:  return (void*)(uintptr_t)mcu_get_leu32(stream);
	default: return (void*)(uintptr_t)mcu_get_leu16(stream);
	}
}

uint64_t mcu_ntoh_u64(uint64_t n) {
	return mcu_get_beu64(&n);
}

uint32_t mcu_ntoh_u32(uint32_t n) {
	return mcu_get_beu32(&n);
}

uint16_t mcu_ntoh_u16(uint16_t n) {
	return mcu_get_beu16(&n);
}

void mcu_reboot(uint32_t reason) {
	// SLCR寄存器，绝对地址
	#define REGADDR_SLCR_UNLOCK 	0xF8000008	// SLCR 解锁寄存器
	#define REGADDR_SLCR_LOCK   	0xF8000004	// SLCR 加锁寄存器
	#define UNLOCK_KEY          	0xDF0D		// 解锁码
	#define LOCK_KEY            	0x767B		// 加锁码
	// 参见 UG585 中 Register (slcr) PSS_RST_CTRL
	#define REGADDR_PSS_RST_CTRL	0xF8000200

	// 解锁
	Xil_Out32(REGADDR_SLCR_UNLOCK, UNLOCK_KEY);
	// asserts PS software reset pulse (entire system except clock generator).
	Xil_Out32(REGADDR_PSS_RST_CTRL, 0x00000001);
	// 加锁
	Xil_Out32(REGADDR_SLCR_LOCK, LOCK_KEY);
}

bool mcu_is_jtag_debug_mode() {
	return false;
}

uint32_t mcu_get_boot_mode() {
	// 参见 UG585 中 Register (slcr) BOOT_MODE
	#define REGADDR_BOOT_MODE 0xF800025C
	uint32_t flag = Xil_In32(REGADDR_BOOT_MODE);
	uint32_t boot_mode  = flag & 0x0F;
	//uint32_t pll_bypass = flag & 0x10;
	return boot_mode;
}

uint32_t mcu_get_reset_reason() {
	// 参见 UG585 中 Register (slcr) REBOOT_STATUS
	// 参见 UG585 中 Table 6‐2: Reboot Status Register
	// The Reboot Status register is reset by a POR reset, but preserved by a non-POR reset.
	#define REGADDR_REBOOT_STATUS 0xF8000258
	#define SUCCESSFUL_JTAG_SLAVE			0x0002 // The system successfully booted in JTAG mode.
	#define LQSPI_PERIPHERAL_INIT_ERROR		0x2000 // QSPI boot failed
	#define NAND_PERIPHERAL_INIT_ERROR		0x2001 // NAND boot failed
	#define SDC_PERIPHERAL_INIT_ERROR		0x200A // SD card boot failed
	#define NOR_PERIPHERAL_INIT_ERROR		0x200B // NOR boot failed
	#define LQSPI_MAX_OFFSET_GIS_HUNT_ERROR	0x200C // QSPI boot failed
	#define NAND_MAX_OFFSET_GIS_HUNT_ERROR	0x200D // SD card boot failed
	#define REGISTER INIT ERROR				0x200E

	uint32_t flag = Xil_In32(REGADDR_REBOOT_STATUS);
	if(flag == SUCCESSFUL_JTAG_SLAVE) {
	} else if(flag == SUCCESSFUL_JTAG_SLAVE) {
	}
	return flag;
}

bool mcu_is_abnormal_reboot() {
	return false;
}

void mcu_watchdog_feed() {
	// TODO
}

const uint8_t* mcu_get_chipid(size_t *size) {
	static const uint8_t id[] = {0,1,2,3,4,5,6,7};
	if(size) *size = sizeof(id);
	return id;
}

extern std_err_t mcu_intc_init();

std_err_t mcu_init(func_bsp_enable_bus_clock bsp_enable_bus_clock, func_bsp_set_irq_priorities bsp_set_irq_priorities) {
	std_err_t err;

	err = mcu_clock_init();
	retonerr(err, "mcu_clock_init() failed ! %s\r\n", std_err_name(err));

	err = mcu_intc_init();
	retonerr(err, "mcu_intc_init() failed ! %s\r\n", std_err_name(err));

	return STD_ERR_OK;
}
